home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / pcxkit.zip / PCX.PAS < prev    next >
Pascal/Delphi Source File  |  1992-01-06  |  27KB  |  676 lines

  1.  
  2. unit PCX;
  3.  
  4. (* version 3.0
  5.                              by Peter Donnelly
  6.                               1301 Ryan Street
  7.                                 Victoria BC
  8.                                Canada V8T 4Y8
  9.  
  10.    ╒══════════════════════════════════════════════════════════════════════╕
  11.    │    May be copied freely. If you make practical use of this unit,     │
  12.    │         a contribution of $10 or more would be appreciated.          │     
  13.    ╘══════════════════════════════════════════════════════════════════════╛
  14.  
  15.    This is a unit to read .PCX files and put them in displayable form. The
  16.    actual work of decoding the file and moving the data into memory is done
  17.    in assembler. Version 6 of Turbo Pascal is required for compilation.
  18.  
  19.    The following display modes are supported:
  20.  
  21.           Mode      TP GraphMode     Resolution    Colors
  22.           ~~~~      ~~~~~~~~~~~~     ~~~~~~~~~~    ~~~~~~
  23.           $04       CGAC0 to C3      320 x 200         4
  24.           $06       CGAHi            640 x 200         2
  25.           $0D        ---             320 x 200        16
  26.           $0E       EGALo/VGALo      640 x 200        16
  27.           $10       EGAHi/VGAMed     640 x 350        16
  28.           $12       VGAHi            640 x 480        16
  29.           $13        ---             320 x 200       256
  30.  
  31.    Mode $13 is supported only for files containing palette information,
  32.    i.e. not those produced by versions of Paintbrush earlier than 3.0.
  33.  
  34.    The unit has been optimized for speed rather than flexibility or
  35.    compactness. In particular, the routine for displaying 16-color files
  36.    (which require the most computation) has been improved and now runs    
  37.    about 50 percent faster than that in version 2.
  38.  
  39.    It is assumed that the image is the width of and no taller than the
  40.    screen, and that you will set the correct display mode. No checking
  41.    is done to see that the .PCX file is compatible with the mode you've set.
  42.    You do, however, have to pass in the Turbo GraphDriver as a parameter
  43.    for all but 256-color files, so that the data will be interpreted
  44.    correctly. (For mode $0D, pass in 'EGA' or 'VGA', and see the comment on
  45.    palettes, below.)
  46.  
  47.    For the CGA formats, the data is put into two buffers on the heap, from
  48.    where it can be moved into the two display memory banks. See SHOWCGA for
  49.    an example. You can of course alter the unit to move the data directly
  50.    into display memory, but there is no great saving in time.
  51.  
  52.    For EGA and VGA formats, the data is written to page 0 of the video
  53.    buffer. This can easily be changed by setting "page_addr" to a different
  54.    value. Three different techniques of hiding the image while it is being
  55.    written are demonstrated in SHOWEGA, SHOWVGA, and SHOW256. If for any
  56.    reason you don't want to do this, you will want to rewrite the palette-
  57.    interpretation routines as separate procedures so you can set the
  58.    palette before decoding the image data.
  59.  
  60.    References:
  61.    ~~~~~~~~~~
  62.    Richard F. Ferraro, "Programmer's Guide to the EGA and VGA Cards"
  63.    (Addison-Wesley, 1988).
  64.  
  65.    Richard Wilton, "Programmer's Guide to PC and PS/2 Video Systems"
  66.    (Microsoft, 1987).
  67.  
  68.    "Technical Reference Manual [for Paintbrush]" (Zsoft, 1988). The
  69.    information in this booklet is also found in a file distributed with
  70.    at least some versions of Microsoft/PC Paintbrush.
  71.  
  72.    Software:
  73.    ~~~~~~~~
  74.    Besides the various incarnations of Paintbrush (ZSoft and Microsoft),
  75.    the excellent Deluxe Paint II Enhanced (Electronic Arts) can also create
  76.    files in .PCX format. Other graphics programs have conversion utilities.
  77. *)
  78.  
  79. (**************************************************************************)
  80.  
  81. interface
  82.  
  83. uses
  84.   Dos,
  85.   Graph;
  86.  
  87. type
  88.   RGBrec = record
  89.              redval,
  90.              greenval,
  91.              blueval : byte
  92.            end;
  93.  
  94. var
  95.   pcxfilename : pathstr;
  96.  
  97.   file_error : boolean;
  98.  
  99.   pal : palettetype;
  100.  
  101.   RGBpal : array[0..15] of RGBrec;
  102.  
  103.   RGB256 : array[0..255] of RGBrec;
  104.  
  105.   page_addr : word;
  106.  
  107.   bytes_per_line : word;
  108.  
  109.   buff0, buff1 : pointer;
  110.  
  111.   (* CGA display memory banks: *)
  112.   screenbuff0 : array[0..7999] of byte absolute $b800 : $0000;
  113.  
  114.   screenbuff1 : array[0..7999] of byte absolute $b800 : $2000;
  115.  
  116. const
  117.   page0 = $A000;                   (* EGA/VGA display segment *)
  118.  
  119.  procedure SETMODE(mode : byte);
  120.  procedure SETREGISTERS(var palrec);
  121.  procedure READ_PCX_FILE(gdriver : integer; pfilename : pathstr);
  122.  procedure READ_PCX256(pfilename : pathstr);
  123.  
  124. (**************************************************************************)
  125.  
  126. implementation
  127.  
  128. var
  129.   scratch,
  130.   abuff0,
  131.   abuff1 : pointer;
  132.  
  133.   is_CGA,
  134.   is_VGA : boolean;
  135.  
  136.   repeatcount : byte;
  137.  
  138.   datalength,
  139.   columncount,
  140.   plane,
  141.   video_index : word;
  142.  
  143.   regs : registers;
  144.  
  145. const
  146.   buffsize = 65521;                (* Largest possible *)
  147.  
  148. (*--------------------------- BIOS calls ---------------------------------*)
  149.  
  150. (* For modes not supported by the BGI, use SetMode to initialize the
  151.   graphics. Since SetRGBPalette won't work if Turbo hasn't done the
  152.   graphics initialization itself, use SetRegisters to change the colors
  153.   in mode $13. *)
  154.  
  155.   procedure SETMODE(mode : byte);
  156.   begin
  157.     regs.ah := 0;                      (* BIOS set mode function *)
  158.     regs.al := mode;                   (* Display mode *)
  159.     intr($10, regs)                    (* Call BIOS *)
  160.   end;
  161.  
  162.             (* Palrec is any string of 768 bytes containing the RGB data. *)
  163.   procedure SETREGISTERS(var palrec);
  164.   begin
  165.     regs.ah := $10;                    (* BIOS color register function *)
  166.     regs.al := $12;                    (* Subfunction *)
  167.     regs.es := seg(palrec);            (* Address of palette info. *)
  168.     regs.dx := ofs(palrec);
  169.     regs.bx := 0;                      (* First register to change *)
  170.     regs.cx := $100;                   (* Number of registers to change *)
  171.     intr($10, regs)                    (* Call BIOS *)
  172.   end;
  173.  
  174. (*********************** EGA/VGA 16-color files *************************)
  175.  
  176.   procedure DECODE_16; assembler;
  177.     asm
  178.  
  179. (* Registers used:
  180.  
  181.    AL   data byte to be written to video
  182.    AH   data bytes per scan line
  183.    BX   end of input buffer
  184.    CL   number of times data byte is to be written
  185.    DL   current column in scan line
  186.    ES   output segment
  187.    DI   index into output buffer
  188.    DS   segment of input buffer
  189.    SI   index into input buffer
  190.    BP   current video plane
  191. *)
  192.  
  193.     push bp
  194.     jmp @startproc
  195.  
  196. (* -------------- Procedure to write EGA/VGA image to video -------------- *)
  197.  
  198. (* The data in the .PCX file is organized by color plane, by line; that is,
  199.   all the data for plane 0 for line 1, then for plane 1, line 1, etc.
  200.   Writing the data to display memory is just a matter of masking out the
  201.   other planes while one plane is being written to. This is done with the
  202.   map mask register in the sequencer. All the other weird and wonderful
  203.   registers in the EGA/VGA do just fine with their default settings, thank
  204.   goodness. *)
  205.  
  206.     @writebyte :
  207.     stosb                              (* AL into ES:DI, inc DI *)
  208.     inc dl                             (* increment column *)
  209.     cmp dl, ah                         (* reached end of scanline? *)
  210.     je @doneline                       (* yes *)
  211.     loop @writebyte                    (* no, do another *)
  212.     ret                                (*   or get more data *)
  213.     @doneline :
  214.     shl bp, 1                          (* shift to next plane *)
  215.     cmp bp, 8                          (* done 4 planes? *)
  216.     jle @setindex                      (* no *)
  217.     mov bp, 1                          (* yes, reset plane to 1 but don't reset index *)
  218.     jmp @setplane
  219.     @setindex :
  220.     sub di, dx                         (* reset to start of line *)
  221.     @setplane :
  222.     push ax                            (* save AX *)
  223.     cli                                (* no interrupts *)
  224.     mov ax, bp                         (* plane is 1, 2, 4, or 8 *)
  225.     mov dx, 3C5h                       (* sequencer data register *)
  226.     out dx, al                         (* mask out 3 planes *)
  227.     sti                                (* enable interrupts *)
  228.     pop ax                             (* restore AX *)
  229.     xor dx, dx                         (* reset column count *)
  230.     loop @writebyte                    (* do it again *)
  231.     ret                                (*   or fetch more data *)
  232.  
  233. (* ------------ Main assembler procedure for 16-color files -------------- *)
  234.  
  235. (* The first section is initialization done on each run through the input
  236.    buffer. *)
  237.  
  238.     @startproc :
  239.     mov bp, plane                      (* plane in BP *)
  240.     mov es, page_addr                  (* video display segment *)
  241.     mov di, video_index                (* index into video segment *)
  242.     mov ah, byte ptr bytes_per_line    (* line length in AH *)
  243.     mov dx, columncount                (* column counter *)
  244.     mov bx, datalength                 (* no. of bytes to read *)
  245.     xor cx, cx                         (* clean up CX for loop counter *)
  246.     mov cl, repeatcount                (* count in CX *)
  247.     push ds                            (* save DS *)
  248.     lds si, scratch                    (* input buffer pointer in DS:SI *)
  249.  
  250. (* We have to adjust datalength for comparison with SI. TP 6.0 pointers are
  251.    normalized, but the offset can still be 0 or 8. *)
  252.  
  253.     add bx, si
  254.     cld                                (* clear DF for stosb *)
  255.  
  256. (* -------------------- Loop through input buffer ------------------------ *)
  257.  
  258. (* Here's how the data compression system works. Each byte is either image
  259.   data or a count byte that tells how often the next image byte is repeated.
  260.   The byte is image data if it follows a count byte, or if either of the top
  261.   two bits is clear. Otherwise it is a count byte, with the count derived
  262.   from the lower 6 bits. *)
  263.  
  264.     @getbyte :
  265.     cmp si, bx                         (* end of input buffer? *)
  266.     je @exit                           (* yes, quit *)
  267.     lodsb                              (* get a byte from DS:SI into AL, increment SI *)
  268.     cmp cl, 0                          (* was prev. byte a count? *)
  269.     jg @multi_data                     (* yes, this is data *)
  270.     cmp al, 192                        (* no, test high bits *)
  271.     jb @one_data                       (* not set, not a count *)
  272.  
  273.     (* It's a count byte: *)
  274.     xor al, 192                        (* get count from 6 low bits *)
  275.     mov cl, al                         (* store repeat count *)
  276.     jmp @getbyte                       (* go get data byte *)
  277.     @one_data :
  278.     mov cl, 1                          (* write byte once *)
  279.     call @writebyte
  280.     jmp @getbyte                       (* fetch another *)
  281.     @multi_data :
  282.     call @writebyte                    (* CL already set *)
  283.     jmp @getbyte                       (* fetch another *)
  284.  
  285. (* ---------------------- Finished with buffer --------------------------- *)
  286.  
  287.     @exit :
  288.     pop ds                             (* restore Turbo's data segment *)
  289.     mov plane, bp                      (* save status for next run thru buffer *)
  290.     mov repeatcount, cl
  291.     mov columncount, dx
  292.     mov video_index, di
  293.     pop bp
  294.     end;                               (* asm *)
  295.  
  296. (********************** CGA 2- and 4-color files *************************)
  297.  
  298.     procedure DECODE_CGA; assembler;
  299.       asm
  300.  
  301. (* Registers used:
  302.  
  303.    AL   data byte to be written to video
  304.    AH   data bytes per scan line
  305.    BX   end of input buffer
  306.    CL   number of times data byte is to be written
  307.    DL   pointer to current column in screen row
  308.    ES   output segment; temporarily used for input buffer segment
  309.    DI   index into output buffer
  310.    SI   index into input buffer
  311.    BP   current video bank
  312. *)
  313.  
  314.       push bp
  315.       jmp @startproc
  316.  
  317. (* ------------- Procedure to store CGA image in buffers ----------------- *)
  318.  
  319.       @storebyte :
  320.       stosb                            (* AL into ES:DI, increment DI *)
  321.       inc dx                           (* increment column count *)
  322.       cmp dl, ah                       (* reached end of line? *)
  323.       je @row_ends                     (* yes *)
  324.       loop @storebyte                  (* not end of row, do another byte *)
  325.       ret
  326.       @row_ends :
  327.       xor bp, 1                        (* switch banks *)
  328.       cmp bp, 1                        (* is bank 1? *)
  329.       je @bank1                        (* yes *)
  330.       mov word ptr abuff1, di          (* no, save index into bank 1 *)
  331.       les di, abuff0                   (* bank 0 pointer into ES:DI *)
  332.       xor dx, dx                       (* reset column counter *)
  333.       loop @storebyte
  334.       ret
  335.       @bank1 :
  336.       mov word ptr abuff0, di          (* save index into bank 0 *)
  337.       les di, abuff1                   (* bank 1 pointer into ES:DI *)
  338.       xor dx, dx                       (* reset column counter *)
  339.       loop @storebyte
  340.       ret
  341.  
  342. (* ---------------- Main assembler procedure for CGA --------------------- *)
  343.  
  344. (* It's assumed that CGA files will require only one pass through the
  345.   input buffer. *)
  346.  
  347.       @startproc :
  348.       mov bp, 0                        (* bank in BP *)
  349.       mov es, word ptr abuff0[2]       (* segment of bank 0 buffer *)
  350.       mov di, word ptr abuff0          (* offset of buffer *)
  351.       mov ah, byte ptr bytes_per_line  (* line length in AH *)
  352.       mov bx, datalength               (* no. of bytes to read *)
  353.       xor cx, cx                       (* clean up CX for loop counter *)
  354.       xor dx, dx                       (* initialize column counter *)
  355.       mov si, dx                       (* initialize input index *)
  356.       cld                              (* clear DF for stosb *)
  357.  
  358. (* -------------------- Loop through input buffer ------------------------ *)
  359.  
  360.       @getbyte :
  361.       cmp si, bx                       (* end of input buffer? *)
  362.       je @exit                         (* yes, quit *)
  363.       push es                          (* save output pointer *)
  364.       push di
  365.       les di, scratch                  (* get input pointer in ES:DI *)
  366.       add di, si                       (* add current offset *)
  367.       mov al, [es : di]                (* get a byte *)
  368.       inc si                           (* advance input index *)
  369.       pop di                           (* restore output pointer *)
  370.       pop es
  371.       cmp cl, 0                        (* was previous byte a count? *)
  372.       jg @multi_data                   (* yes, this is data *)
  373.       cmp al, 192                      (* no, test high bits *)
  374.       jb @one_data                     (* not set, not a count *)
  375.  
  376.       (* It's a count byte: *)
  377.       xor al, 192                      (* get count from 6 low bits *)
  378.       mov cl, al                       (* store repeat count *)
  379.       jmp @getbyte                     (* go get data byte *)
  380.       @one_data :
  381.       mov cl, 1                        (* write byte once *)
  382.       call @storebyte
  383.       jmp @getbyte
  384.       @multi_data :
  385.       call @storebyte                  (* CL already set *)
  386.       jmp @getbyte
  387.  
  388. (* ---------------------- Finished with buffer --------------------------- *)
  389.  
  390.       @exit :
  391.       pop bp
  392.       end;                             (* asm *)
  393.  
  394. (************* Main procedure for CGA and 16-color files ***************)
  395.  
  396.       procedure READ_PCX_FILE(gdriver : integer; pfilename : pathstr);
  397.       type
  398.         ptrrec = record
  399.                    segm,
  400.                    offs : word
  401.                  end;
  402.  
  403.       var
  404.         entry,
  405.         gun,
  406.         pcxcode,
  407.         mask,
  408.         colorID : byte;
  409.         palbuf : array[0..66] of byte;
  410.         pcxfile : file;
  411.       begin                            (* READ_PCX_FILE *)
  412.         is_CGA := (gdriver = CGA);     (* 2 or 4 colors *)
  413.         is_VGA := (gdriver = VGA);     (* 16 of 256K possible colors *)
  414.         (* Otherwise EGA - 16 of 64 possible colors *)
  415.         assign(pcxfile, pfilename);
  416.         {$I-}
  417.         reset(pcxfile, 1);
  418.         {$I+}
  419.         file_error := (ioresult <> 0);
  420.         if file_error then
  421.           exit;
  422.  
  423. (* To minimize disk access and speed things up, we read the file into a
  424.   scratchpad on the heap. Large files have to be done in two or more
  425.   chunks because of the 64K limit on dynamic memory variables. *)
  426.  
  427.         getmem(scratch, buffsize);     (* Allocate scratchpad *)
  428.         blockread(pcxfile, scratch^, 128); (* Get header into scratchpad *)
  429.  
  430. (* The .PCX file has a 128-byte header. Most of it can be ignored if you're
  431.   working with a known format. All we want is the palette information and
  432.   the length of the data line. *)
  433.  
  434.         move(scratch^, palbuf, 67);
  435.         bytes_per_line := palbuf[66];
  436.  
  437. (*------------------------ Setup for CGA ---------------------------------*)
  438.  
  439.         if is_CGA then
  440.           begin
  441.             getmem(buff0, 8000);       (* Allocate memory for output *)
  442.             getmem(buff1, 8000);
  443.             abuff0 := buff0;           (* Make copies of pointers *)
  444.             abuff1 := buff1;           
  445.           end
  446.         else
  447.  
  448. (*----------------------- Setup for EGA/VGA ------------------------------*)
  449.           begin
  450.             video_index := 0;
  451.             port[$3C4] := 2;           (* Index to map mask register *)
  452.             plane := 1;                (* Initialize plane *)
  453.             port[$3C5] := plane;       (* Set sequencer to mask out other planes *)
  454.  
  455. (*-------------------- Decipher EGA/VGA palette --------------------------*)
  456.  
  457. (* The palette information is stored in bytes 16-63 of the header. Each of
  458.    the 16 palette slots is allotted 3 bytes - one for each primary color.
  459.    Any of these bytes can have a value of 0-255.
  460.  
  461.    For the EGA there are just 4 significant settings, since only 64
  462.    different colors (4 x 4 x 4) are available. Hence for EGA-format images
  463.    we divide the codes by 64. The absolute color number for the palette
  464.    entry is derived by setting one of bits 0-2 and one of bits 3-5 with the
  465.    mask corresponding to the .PCX code byte. (In binary form, the absolute
  466.    color number may be thought of as 00RGBrgb.) This number is then passed
  467.    into Turbo's SetAllPalette procedure.
  468.  
  469.    For the VGA things work differently. Here we must use Turbo's
  470.    SetRGBPalette procedure to change the red, green, and blue values in the
  471.    16 active color registers. The registers expect values in the range 0-63
  472.    (64 x 64 x 64 = 256K, the number of possible colors), so we divide the
  473.    .PCX codes by 4. A further complication is that by default the palette
  474.    entries point to the color registers corresponding to the standard EGA
  475.    colors, so we must change them to point to registers 0-15 instead (or
  476.    else modify registers 0-5, 20, 7, and 56-63). See SHOWVGA.PAS for an
  477.    example of how to set the palette and the registers.
  478.  
  479.    Note that the palette works differently for the 200-line 16-color modes,
  480.    $0D and $0E. Because these modes use 4-bit palette entries, only the
  481.    default colors are available on the EGA, and their IDs don't correspond
  482.    to those in 350-line mode (e.g. 20 is bright red, not brown). Attempting
  483.    to set the palette with the data from the .PCX header will lead to odd
  484.    results in these modes, and in any case should not be necessary.
  485. *)
  486.  
  487.             for entry := 0 to 15 do
  488.               begin
  489.                 colorID := 0;
  490.                 for gun := 0 to 2 do
  491.                   begin
  492.                        (* Get primary color value *)
  493.                     pcxcode := palbuf[16 + entry * 3 + gun]; 
  494.                     if not is_VGA then
  495.                       begin            (* Interpret for EGA *)
  496.                         case (pcxcode div $40) of
  497.                           0 : mask := $00; (* 000000 *)
  498.                           1 : mask := $20; (* 100000 *)
  499.                           2 : mask := $04; (* 000100 *)
  500.                           3 : mask := $24  (* 100100 *)
  501.                         end;
  502.                         colorID := colorID or (mask shr gun); (* Define two bits *)
  503.                       end              (* not is_VGA *)
  504.                     else
  505.                       begin            (* is_VGA *)
  506.                         with RGBpal[entry] do (* Interpret for VGA *)
  507.                           case gun of
  508.                             0 : redval := pcxcode div 4;
  509.                             1 : greenval := pcxcode div 4;
  510.                             2 : blueval := pcxcode div 4;
  511.                           end
  512.                       end              (* is_VGA *)
  513.                   end;                 (* gun *)
  514.                 if is_VGA then
  515.                   pal.colors[entry] := entry
  516.                 else
  517.                   pal.colors[entry] := colorID
  518.               end;                     (* entry *)
  519.             pal.size := 16
  520.           end;                         (* not is_CGA *)
  521.  
  522. (* ---------------- Read and decode the image data ----------------------- *)
  523.  
  524.         repeatcount := 0;              (* Initialize assembler vars. *)
  525.         columncount := 0;
  526.         repeat
  527.           blockread(pcxfile, scratch^, buffsize, datalength);
  528.           if is_CGA then
  529.             DECODE_CGA
  530.           else
  531.             DECODE_16; (* Call assembler routine *)
  532.         until eof(pcxfile);
  533.         close(pcxfile);
  534.         if not is_CGA then
  535.           port[$3C5] := $F; (* Reset mask map *)
  536.         freemem(scratch, buffsize);    (* Discard scratchpad *)
  537.       end;                             (* READ_PCX_FILE *)
  538.  
  539. (************************** 256-color files *****************************)
  540.  
  541.       procedure DECODE_PCX256; assembler;
  542.  
  543. (* Registers used:
  544.  
  545.    AL   data byte to be written to video
  546.    BX   end of input buffer
  547.    CL   number of times data byte is to be written
  548.    ES   output segment
  549.    DI   index into output buffer
  550.    DS   segment of input buffer
  551.    SI   index into input buffer
  552. *)
  553.  
  554.         asm
  555.         mov es, page_addr              (* video segment *)
  556.         mov di, video_index            (* index into video *)
  557.         xor cx, cx                     (* clean up loop counter *)
  558.         mov cl, repeatcount            (* count in CL *)
  559.         mov bx, datalength             (* end of input buffer *)
  560.         push ds                        (* save DS *)
  561.         lds si, scratch                (* pointer to input in DS:SI *)
  562.         add bx, si                     (* adjust datalength - SI may not be 0 *)
  563.         cld                            (* clear DF *)
  564.  
  565. (* --------------------- Loop through input buffer ----------------------- *)
  566.  
  567.         @getbyte :
  568.         cmp si, bx                     (* end of input buffer? *)
  569.         je @exit                       (* yes, quit *)
  570.         lodsb                          (* get byte into AL, increment SI *)
  571.         cmp cl, 0                      (* was prev. byte a count? *)
  572.         jg @multi_data                 (* yes, this is data *)
  573.         cmp al, 192                    (* no, test high bits *)
  574.         jb @one_data                   (* not set, not a count *)
  575.  
  576.         (* It's a count byte *)
  577.         xor al, 192                    (* get count from 6 low bits *)
  578.         mov cl, al                     (* store repeat count *)
  579.         jmp @getbyte                   (* go get data byte *)
  580.         @one_data :
  581.         stosb                          (* byte into video *)
  582.         jmp @getbyte
  583.         @multi_data :
  584.         rep stosb                      (* write byte CX times *)
  585.         jmp @getbyte
  586.  
  587. (* ------------------------- Finished with buffer ------------------------ *)
  588.  
  589.         @exit :
  590.         pop ds                         (* restore Turbo's data segment *)
  591.         mov video_index, di            (* save status for next run thru buffer *)
  592.         mov repeatcount, cl
  593.         end;                           (* asm *)
  594.  
  595. (****************** Main procedure for 256-color files *******************)
  596.  
  597.         procedure READ_PCX256(pfilename : pathstr);
  598.         var
  599.           x,
  600.           gun,
  601.           pcxcode : byte;
  602.           pcxfile : file;
  603.           palette_start,
  604.           total_read : longint;
  605.           palette_flag : byte;
  606.           version : word;
  607.  
  608.           procedure CLEANUP;
  609.           begin
  610.             close(pcxfile);
  611.             freemem(scratch, buffsize)
  612.           end;
  613.  
  614.         begin                          (* READ_PCX256 *)
  615.           assign(pcxfile, pfilename);
  616.           {$I-}
  617.           reset(pcxfile, 1);
  618.           {$I+}
  619.           file_error := (ioresult <> 0);
  620.           if file_error then
  621.             exit;
  622.           getmem(scratch, buffsize);   (* Allocate scratchpad *)
  623.           blockread(pcxfile, version, 2); (* Read first two bytes *)
  624.           file_error := (hi(version) < 5); (* No palette info. *)
  625.           if file_error then
  626.             begin
  627.               CLEANUP;
  628.               exit
  629.             end;
  630.           palette_start := filesize(pcxfile) - 769;
  631.  
  632.           seek(pcxfile, 128);          (* Scrap file header *)
  633.           total_read := 128;
  634.  
  635.           repeatcount := 0;            (* Initialize assembler vars. *)
  636.           video_index := 0;
  637.  
  638.           repeat
  639.             blockread(pcxfile, scratch^, buffsize, datalength);
  640.             inc(total_read, datalength);
  641.             if (total_read > palette_start) then
  642.               dec(datalength, total_read - palette_start);
  643.             DECODE_PCX256;
  644.           until (eof(pcxfile)) or (total_read >= palette_start);
  645.  
  646. (* The last 769 btes of the file are palette information, starting with a
  647.    one-byte flag. Each group of three bytes represents the RGB values of
  648.    one of the color registers. The values have to be divided by 4 to be
  649.    brought within the range 0-63 expected by the registers. *)
  650.  
  651.           seek(pcxfile, palette_start);
  652.           blockread(pcxfile, palette_flag, 1);
  653.           file_error := (palette_flag <> 12);
  654.           if file_error then
  655.             begin
  656.               CLEANUP;
  657.               exit
  658.             end;
  659.           blockread(pcxfile, RGB256, 768); (* Get palette info. *)
  660.           for x := 0 to 255 do
  661.             with RGB256[x] do
  662.               begin
  663.                 redval := redval shr 2;
  664.                 greenval := greenval shr 2;
  665.                 blueval := blueval shr 2
  666.               end;
  667.           CLEANUP
  668.         end;                           (* READ_PCX256 *)
  669.  
  670. (*************************** Initialization *****************************)
  671.  
  672. BEGIN
  673.   page_addr := page0             (* Destination for EGA/VGA data *)
  674. END.
  675.  
  676.